package com.galaxy.device.utils;

import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * ES工具类
 */
@Slf4j
@Component
public class EsWrappersUtils {

    private static RestHighLevelClient restHighLevelClient;

    public EsWrappersUtils(RestHighLevelClient restHighLevelClient) {
        EsWrappersUtils.restHighLevelClient = restHighLevelClient;
    }

    /**
     * 判断索引是否存在
     *
     * @param indexName 索引名称
     * @return 是否存在
     */
    public static boolean existsIndex(String indexName) {
        try {
            GetIndexRequest request = new GetIndexRequest();
            request.indices(indexName);
            return restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("检查索引是否存在失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 创建索引
     *
     * @param indexName 索引名称
     * @param mapping   映射配置
     * @return 是否创建成功
     */
    public static boolean createIndex(String indexName, Map<String, Object> mapping) {
        try {
            CreateIndexRequest request = new CreateIndexRequest(indexName);
            // 设置分片数和副本数
            request.settings(Settings.builder()
                .put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 1)
            );
            // 设置映射
            request.mapping("_doc", mapping, XContentType.JSON);
            CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
            return response.isAcknowledged();
        } catch (IOException e) {
            log.error("创建索引失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 删除索引
     *
     * @param indexName 索引名称
     * @return 是否删除成功
     */
    public static boolean deleteIndex(String indexName) {
        try {
            if (existsIndex(indexName)) {
                restHighLevelClient.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT);
                return true;
            }
            return false;
        } catch (IOException e) {
            log.error("删除索引失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 构建地理位置距离查询
     *
     * @param fieldName  地理位置字段名
     * @param latitude   纬度
     * @param longitude  经度
     * @param distance   距离
     * @param unit       距离单位
     * @return QueryBuilder
     */
    public static QueryBuilder buildGeoDistanceQuery(String fieldName, double latitude, double longitude, double distance, DistanceUnit unit) {
        return QueryBuilders.geoDistanceQuery(fieldName)
            .point(latitude, longitude)
            .distance(distance, unit);
    }

    /**
     * 构建地理位置距离查询（简化版，默认使用千米作为单位）
     *
     * @param fieldName  地理位置字段名
     * @param latitude   纬度
     * @param longitude  经度
     * @param distance   距离（千米）
     * @return QueryBuilder
     */
    public static QueryBuilder buildGeoDistanceQuery(String fieldName, double latitude, double longitude, double distance) {
        return buildGeoDistanceQuery(fieldName, latitude, longitude, distance, DistanceUnit.KILOMETERS);
    }

    /**
     * 执行查询
     *
     * @param indexName    索引名称
     * @param queryBuilder 查询条件
     * @return 查询结果列表
     */
    public static List<Map<String, Object>> list(String indexName, QueryBuilder queryBuilder) {
        try {
            SearchRequest searchRequest = new SearchRequest(indexName);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(queryBuilder);
            searchRequest.source(searchSourceBuilder);
            
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            
            List<Map<String, Object>> resultList = new ArrayList<>();
            for (SearchHit hit : response.getHits().getHits()) {
                resultList.add(hit.getSourceAsMap());
            }
            return resultList;
        } catch (IOException e) {
            log.error("查询文档失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    /**
     * 执行分页查询
     *
     * @param indexName    索引名称
     * @param queryBuilder 查询条件
     * @param pageNum      页码
     * @param pageSize     每页大小
     * @return 查询结果列表
     */
    public static List<Map<String, Object>> page(String indexName, QueryBuilder queryBuilder, Integer pageNum, Integer pageSize) {
        try {
            SearchRequest searchRequest = new SearchRequest(indexName);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(queryBuilder);
            searchSourceBuilder.from((pageNum - 1) * pageSize);
            searchSourceBuilder.size(pageSize);
            searchRequest.source(searchSourceBuilder);
            
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            
            List<Map<String, Object>> resultList = new ArrayList<>();
            for (SearchHit hit : response.getHits().getHits()) {
                resultList.add(hit.getSourceAsMap());
            }
            return resultList;
        } catch (IOException e) {
            log.error("分页查询文档失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    /**
     * 获取文档总数
     *
     * @param indexName    索引名称
     * @param queryBuilder 查询条件
     * @return 文档总数
     */
    public static long count(String indexName, QueryBuilder queryBuilder) {
        try {
            SearchRequest searchRequest = new SearchRequest(indexName);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            searchSourceBuilder.query(queryBuilder);
            searchRequest.source(searchSourceBuilder);
            
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            return response.getHits().getTotalHits().value;
        } catch (IOException e) {
            log.error("获取文档总数失败: {}", e.getMessage());
            return 0;
        }
    }

    /**
     * 计算两点之间的距离
     *
     * @param lat1 第一个点的纬度
     * @param lon1 第一个点的经度
     * @param lat2 第二个点的纬度
     * @param lon2 第二个点的经度
     * @param unit 距离单位
     * @return 距离
     */
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2, DistanceUnit unit) {
        // 地球半径（米）
        final double EARTH_RADIUS = 6371000;
        
        // 将经纬度转换为弧度
        double lat1Rad = Math.toRadians(lat1);
        double lon1Rad = Math.toRadians(lon1);
        double lat2Rad = Math.toRadians(lat2);
        double lon2Rad = Math.toRadians(lon2);
        
        // 计算差值
        double dLat = lat2Rad - lat1Rad;
        double dLon = lon2Rad - lon1Rad;
        
        // 使用Haversine公式计算距离
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(lat1Rad) * Math.cos(lat2Rad) *
            Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        double distance = EARTH_RADIUS * c;
        
        // 根据单位转换距离
        if (unit == DistanceUnit.KILOMETERS) {
            return distance / 1000;
        } else if (unit == DistanceUnit.MILES) {
            return distance / 1609.344;
        } else {
            return distance;
        }
    }

    /**
     * 根据ID获取文档
     *
     * @param indexName 索引名称
     * @param id        文档ID
     * @return 文档数据
     */
    public static Map<String, Object> getById(String indexName, String id) {
        try {
            GetRequest getRequest = new GetRequest(indexName, id);
            GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            return response.getSourceAsMap();
        } catch (IOException e) {
            log.error("获取文档失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 根据ID更新文档
     *
     * @param indexName 索引名称
     * @param id        文档ID
     * @param data      更新数据
     * @return 是否成功
     */
    public static boolean updateById(String indexName, String id, Map<String, Object> data) {
        try {
            UpdateRequest request = new UpdateRequest(indexName, id);
            request.doc(data, XContentType.JSON);
            return restHighLevelClient.update(request, RequestOptions.DEFAULT).getResult().name().equals("UPDATED");
        } catch (IOException e) {
            log.error("更新文档失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 根据ID删除文档
     *
     * @param indexName 索引名称
     * @param id        文档ID
     * @return 是否成功
     */
    public static boolean deleteById(String indexName, String id) {
        try {
            DeleteRequest request = new DeleteRequest(indexName, id);
            return restHighLevelClient.delete(request, RequestOptions.DEFAULT).getResult().name().equals("DELETED");
        } catch (IOException e) {
            log.error("删除文档失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 批量插入文档
     *
     * @param indexName 索引名称
     * @param dataList  数据列表
     * @return 是否成功
     */
    public static boolean batchInsert(String indexName, List<Map<String, Object>> dataList) {
        try {
            BulkRequest request = new BulkRequest();
            for (Map<String, Object> data : dataList) {
                String id = (String) data.get("deviceId");
                IndexRequest indexRequest = new IndexRequest(indexName)
                    .id(id)
                    .source(data, XContentType.JSON);
                request.add(indexRequest);
            }
            BulkResponse response = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            return !response.hasFailures();
        } catch (IOException e) {
            log.error("批量插入文档失败: {}", e.getMessage());
            return false;
        }
    }
}
 